home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2000 #4 / Amiga Plus CD - 2000 - No. 4.iso / Vollversion / CamD / development / examples / capture / capture.c < prev    next >
C/C++ Source or Header  |  2000-05-15  |  22KB  |  837 lines

  1.  
  2. /* Notes: Wouldn't it be better to have the internal representation be
  3.     standard MIDI (i.e. no time delays?)
  4. */
  5.  
  6. /* Includes */
  7.  
  8. #include <exec/types.h>
  9. #include <intuition/intuition.h>
  10. #include <dos/dos.h>
  11. #include <string.h>
  12. #include <stdlib.h>
  13. #include <libraries/asl.h>
  14. #include <midi/camd.h>
  15. #include <midi/mididefs.h>
  16. #include <clib/macros.h>
  17.  
  18. #include "/camdlistreq/listreq.h"
  19.  
  20. #include "capture.h"    /* User header file */
  21. #include "capture_rev.h"    /* Bumprev header file */
  22.  
  23. #include <clib/exec_protos.h>
  24. #include <clib/intuition_protos.h>
  25. #include <clib/gadtools_protos.h>
  26. #include <clib/utility_protos.h>
  27. #include <clib/asl_protos.h>
  28. #include <clib/dos_protos.h>
  29. #include <clib/alib_protos.h>
  30. #include <clib/camd_protos.h>
  31.  
  32. /* Pragma includes for register parameters */
  33.  
  34. #include <pragmas/exec_pragmas.h>
  35. #include <pragmas/intuition_pragmas.h>
  36. #include <pragmas/gadtools_pragmas.h>
  37. #include <pragmas/utility_pragmas.h>
  38. #include <pragmas/asl_pragmas.h>
  39. #include <pragmas/dos_pragmas.h>
  40. #include <pragmas/camd_pragmas.h>
  41.  
  42. /* Global variables */
  43.  
  44. char vers[] = VERSTAG;    /* Bumprev version string */
  45. struct Library *IntuitionBase;    /* intuition.library pointer */
  46. struct Library *GadToolsBase;    /* gadtools.library pointer */
  47. struct Library *UtilityBase;    /* utility.library pointer */
  48. struct Library *AslBase;    /* asl.library pointer */
  49. struct Library *CamdBase;        /* camd.library pointer */
  50. struct IntuiText BodyText = {0,1,JAM2,20,8,NULL,(UBYTE *)TEXT_NORELEASE2,NULL};
  51. struct IntuiText NegText  = {0,1,JAM2, 6,4,NULL,(UBYTE *)TEXT_OK,NULL};
  52.  
  53. struct List                memory_list;
  54.  
  55. #define BLOCK_SIZE        2048
  56.  
  57. #define SYSEX_BUFFER_SIZE 2048
  58.  
  59. struct memory_block {
  60.     struct MinNode        node;
  61.     LONG                filled;
  62.     UBYTE                data[ BLOCK_SIZE ];
  63. };
  64.  
  65. LONG                    buffer_size = 0;
  66. UBYTE                    buffer_running_status = 0,
  67.                         read_running_status = 0;
  68. LONG                    buffer_time = 0;        /* future use                */
  69. BOOL                    has_end_event = FALSE;
  70.  
  71. struct MidiNode            *midi;
  72. struct MidiLink            *in_link,
  73.                         *out_link;
  74.  
  75. struct ListReq            *lreq;
  76.  
  77. struct MidiNode *CreateMidi(Tag tag, ...)
  78. {    return CreateMidiA((struct TagItem *)&tag );
  79. }
  80.  
  81. BOOL SetMidiAttrs(struct MidiNode *mi, Tag tag, ...)
  82. {    return SetMidiAttrsA(mi, (struct TagItem *)&tag );
  83. }
  84.  
  85. struct MidiLink *AddMidiLink(struct MidiNode *mi, LONG type, Tag tag, ...)
  86. {    return AddMidiLinkA(mi, type, (struct TagItem *)&tag );
  87. }
  88.  
  89. BOOL SetMidiLinkAttrs(struct MidiLink *mi, Tag tag, ...)
  90. {    return SetMidiLinkAttrsA(mi, (struct TagItem *)&tag );
  91. }
  92.  
  93. ULONG GetMidiLinkAttrs(struct MidiLink *mi, Tag tag, ...)
  94. {    return GetMidiLinkAttrsA(mi, (struct TagItem *)&tag );
  95. }
  96.  
  97. /* Entry functions */
  98.  
  99. #ifdef _DCC    /* for DICE compatibility */
  100. VOID wbmain(VOID *wbmsg)
  101.   {
  102.   main(0, wbmsg);
  103.   }
  104. #endif
  105.  
  106. VOID main(int argc, char **argv)
  107.   {
  108.   struct TMData *TMData;    /* data structure pointer */
  109.   ULONG error;
  110.  
  111.     NewList( &memory_list );
  112.  
  113.   if(!(IntuitionBase = OpenLibrary((UBYTE *)"intuition.library", 37L)))    /* Open intuition.library V37 */
  114.     {
  115.     if(IntuitionBase = OpenLibrary((UBYTE *)"intuition.library", 0L))
  116.       {
  117.       AutoRequest(NULL, &BodyText, NULL, &NegText, 0, 0, 320, 80);
  118.       CloseLibrary(IntuitionBase);
  119.       }
  120.     cleanexit(NULL, RETURN_FAIL);
  121.     }
  122.  
  123.   if(!(GadToolsBase = OpenLibrary((UBYTE *)"gadtools.library", 37L)))    /* Open gadtools.library V37 */
  124.     {
  125.     TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOLIBRARY, (UBYTE *)TEXT_ABORT, NULL, "gadtools.library V37");
  126.     cleanexit(NULL, RETURN_FAIL);
  127.     }
  128.  
  129.   if(!(UtilityBase = OpenLibrary((UBYTE *)"utility.library", 37L)))    /* Open utility.library V37 */
  130.     {
  131.     TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOLIBRARY, (UBYTE *)TEXT_ABORT, NULL, "utility.library V37");
  132.     cleanexit(NULL, RETURN_FAIL);
  133.     }
  134.  
  135.   if(!(AslBase = OpenLibrary((UBYTE *)"asl.library", 37L)))    /* Open asl.library V37 */
  136.     {
  137.     TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOLIBRARY, (UBYTE *)TEXT_ABORT, NULL, "asl.library V37");
  138.     cleanexit(NULL, RETURN_FAIL);
  139.     }
  140.  
  141.   if(!(CamdBase = OpenLibrary((UBYTE *)"camd.library", 0L)))    /* Open camd.library */
  142.     {
  143.     TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOLIBRARY, (UBYTE *)TEXT_ABORT, NULL, "camd.library");
  144.     cleanexit(NULL, RETURN_FAIL);
  145.     }
  146.  
  147.     if (!(midi = CreateMidi(    MIDI_Name,         "MIDI Capture",
  148.                                 MIDI_RecvSignal,SIGBREAKB_CTRL_E,
  149.                                 MIDI_MsgQueue,     200,
  150.                                 MIDI_SysExSize,    SYSEX_BUFFER_SIZE,
  151.                                 TAG_DONE )))
  152.     {    TM_Request(NULL, (UBYTE *)TEXT_ERROR, "Can't Create the MIDI Node.", (UBYTE *)TEXT_ABORT, NULL, NULL);
  153.         cleanexit(NULL, RETURN_FAIL);
  154.     }
  155.  
  156.     if (!(out_link = AddMidiLink (midi, MLTYPE_Sender,
  157.                                     MLINK_Location, "out.0",
  158.                                     MLINK_Name,     "Out",
  159.                                     TAG_END )))
  160.     {    TM_Request(NULL, (UBYTE *)TEXT_ERROR, "Can't establish output link.", (UBYTE *)TEXT_ABORT, NULL, NULL);
  161.         cleanexit(NULL, RETURN_FAIL);
  162.     }
  163.  
  164.     if (!(in_link = AddMidiLink (    midi, MLTYPE_Receiver,
  165.                                     MLINK_Location, "in.0",
  166.                                     MLINK_Name,        "In",
  167.                                     TAG_END )))
  168.     {    TM_Request(NULL, (UBYTE *)TEXT_ERROR, "Can't establish input link.", (UBYTE *)TEXT_ABORT, NULL, NULL );
  169.         cleanexit(NULL, RETURN_FAIL);
  170.     }
  171.  
  172.     if (!(lreq = AllocListRequest( TAG_END )))
  173.     {    TM_Request(NULL, (UBYTE *)TEXT_ERROR, "Error creating cluster list requester.", (UBYTE *)TEXT_ABORT, NULL, NULL );
  174.         cleanexit(NULL, RETURN_FAIL);
  175.     }
  176.  
  177.   if(!(TMData = TM_Open(&error)))    /* Open Toolmaker data */
  178.     {
  179.     switch(error)
  180.       {
  181.       case TMERR_MEMORY:
  182.         TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOMEMORY, (UBYTE *)TEXT_ABORT, NULL, NULL);
  183.         break;
  184.       case TMERR_MSGPORT:
  185.         TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOMSGPORT, (UBYTE *)TEXT_ABORT, NULL, NULL);
  186.         break;
  187.       }
  188.     cleanexit(NULL, RETURN_FAIL);
  189.     }
  190.  
  191.   if(!(TMData->FileRequester = AllocAslRequestTags(ASL_FileRequest, TAG_DONE)))
  192.     {
  193.     TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOFILEREQ, (UBYTE *)TEXT_ABORT, NULL, NULL);
  194.     cleanexit(TMData, RETURN_FAIL);
  195.     }
  196.  
  197.   if(!(OpenScreen_Workbench(TMData)))    /* Open default public screen */
  198.     {
  199.     TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOSCREEN, (UBYTE *)TEXT_ABORT, NULL, NULL);
  200.     cleanexit(TMData, RETURN_FAIL);
  201.     }
  202.  
  203.   if(!(OpenWindow_CAPTUREM(TMData)))    /* Open the window */
  204.     {
  205.     TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOWINDOW, (UBYTE *)TEXT_ABORT, NULL, NULL);
  206.     cleanexit(TMData, RETURN_FAIL);
  207.     }
  208.  
  209.  
  210.  
  211.  
  212.   TM_EventLoop(TMData);    /* Process events */
  213.  
  214.   cleanexit(TMData, RETURN_OK);
  215.   }
  216.  
  217. /* cleanexit function */
  218.  
  219. VOID cleanexit(struct TMData *TMData, int returnvalue)
  220. {
  221.     if(TMData)
  222.     {
  223.         CloseWindow_CAPTUREM(TMData);
  224.         CloseScreen_Workbench(TMData);
  225.         if(TMData->FileRequester) FreeAslRequest(TMData->FileRequester);
  226.         TM_Close(TMData);
  227.     }
  228.  
  229.     if (out_link)        RemoveMidiLink ( out_link );
  230.     if (in_link)        RemoveMidiLink ( in_link );
  231.     if (midi)            DeleteMidi( midi );
  232.     if (lreq)            FreeListRequest( lreq );
  233.  
  234.     if(CamdBase)        CloseLibrary(CamdBase);        /* Close camd.library        */
  235.     if(AslBase)            CloseLibrary(AslBase);        /* Close asl.library        */
  236.     if(UtilityBase)        CloseLibrary(UtilityBase);    /* Close utility.library    */
  237.     if(GadToolsBase)    CloseLibrary(GadToolsBase);    /* Close gadtools.library    */
  238.     if(IntuitionBase)    CloseLibrary(IntuitionBase);/* Close intuition.library    */
  239.  
  240.     exit(returnvalue);
  241. }
  242.  
  243. /* ASL file requester function */
  244.  
  245. UBYTE *getfilename(struct TMData *TMData, UBYTE *title, UBYTE *buffer, ULONG bufsize, struct Window *window, BOOL saveflag)
  246.   {
  247.   ULONG funcflags;
  248.  
  249.   if(saveflag)
  250.     funcflags = FILF_SAVE;
  251.   else
  252.     funcflags = 0;
  253.  
  254.   if(buffer && TMData)
  255.     {
  256.     if(AslRequestTags((APTR) TMData->FileRequester,
  257.                       ASL_Hail, title,
  258.                       ASL_Window, window,
  259.                       ASL_FuncFlags, funcflags,
  260.                       TAG_DONE))
  261.       {
  262.       strcpy(buffer, TMData->FileRequester->rf_Dir);
  263.       if(AddPart(buffer, TMData->FileRequester->rf_File, bufsize))
  264.         {
  265.         return(buffer);
  266.         }
  267.       }
  268.     }
  269.  
  270.   return(NULL);
  271.   }
  272.  
  273. /* Window event functions */
  274.  
  275. VOID Window_CAPTUREM_REFRESHWINDOW(struct TMData *TMData, struct IntuiMessage *imsg)
  276.   {
  277.   }
  278.  
  279. BOOL Window_CAPTUREM_CLOSEWINDOW(struct TMData *TMData, struct IntuiMessage *imsg)
  280.   {
  281.   return(TRUE);
  282.   }
  283.  
  284. BOOL Window_CAPTUREM_MENUPICK(struct TMData *TMData, struct IntuiMessage *imsg)
  285.   {
  286.   UWORD menucode;
  287.   struct MenuItem *menuitem;
  288.   TMOBJECTDATA *tmobjectdata;
  289.   BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);
  290.  
  291.   menucode = imsg->Code;
  292.   while(menucode != MENUNULL)
  293.     {
  294.     menuitem = ItemAddress(WindowInfo_CAPTUREM.Menu, menucode);
  295.     tmobjectdata = (TMOBJECTDATA *)(GTMENUITEM_USERDATA(menuitem));
  296.     eventfunc = tmobjectdata->EventFunc;
  297.  
  298.     if(eventfunc)
  299.       {
  300.       if((*eventfunc)(TMData, imsg, tmobjectdata)) return(TRUE);
  301.       }
  302.  
  303.     menucode = menuitem->NextSelect;
  304.     }
  305.   return(FALSE);
  306.   }
  307.  
  308. BOOL Window_CAPTUREM_GADGETDOWN(struct TMData *TMData, struct IntuiMessage *imsg)
  309.   {
  310.   struct Gadget *gadget;
  311.   TMOBJECTDATA *tmobjectdata;
  312.   BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);
  313.  
  314.   gadget = (struct Gadget *)imsg->IAddress;
  315.   tmobjectdata = (TMOBJECTDATA *)(gadget->UserData);
  316.   eventfunc = tmobjectdata->EventFunc;
  317.  
  318.   if(eventfunc)
  319.     {
  320.     return((*eventfunc)(TMData, imsg, tmobjectdata));
  321.     }
  322.   return(FALSE);
  323.   }
  324.  
  325. BOOL Window_CAPTUREM_GADGETUP(struct TMData *TMData, struct IntuiMessage *imsg)
  326.   {
  327.   struct Gadget *gadget;
  328.   TMOBJECTDATA *tmobjectdata;
  329.   BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);
  330.  
  331.   gadget = (struct Gadget *)imsg->IAddress;
  332.   tmobjectdata = (TMOBJECTDATA *)(gadget->UserData);
  333.   eventfunc = tmobjectdata->EventFunc;
  334.  
  335.   if(eventfunc)
  336.     {
  337.     return((*eventfunc)(TMData, imsg, tmobjectdata));
  338.     }
  339.   return(FALSE);
  340.   }
  341.  
  342.     /* Buffer management functions */
  343.  
  344. LONG LengthVarNum( ULONG num )
  345. {    LONG                length = 0;
  346.  
  347.     do { length++; num >>= 7; } while (num) ;
  348.     return length;
  349. }
  350.  
  351. void ClearBuffer( void )
  352. {    struct memory_block    *mb;
  353.  
  354.     while (mb = (struct memory_block *)RemHead( &memory_list ))
  355.     {    FreeMem( mb, sizeof *mb );
  356.     }
  357.     buffer_size = 0;
  358.     buffer_running_status = 0;
  359.     buffer_time = 0;
  360.     has_end_event = FALSE;
  361. }
  362.  
  363. BOOL AppendBytes( UBYTE *data, LONG length )
  364. {    struct memory_block    *mb;
  365.  
  366.     while (length > 0)
  367.     {    LONG            fit;
  368.  
  369.             /*    Get last list node */
  370.  
  371.         mb = (struct memory_block *)memory_list.lh_TailPred;
  372.  
  373.             /*    If empty list, or block full, allocate new block */
  374.  
  375.         if (mb->node.mln_Pred == NULL ||
  376.             mb->filled >= BLOCK_SIZE)
  377.         {
  378.             if (!(mb = AllocMem( sizeof *mb, 0L )))
  379.             {    return FALSE;
  380.             }
  381.             mb->filled = 0;
  382.             AddTail( &memory_list, (struct Node *)mb );
  383.         }
  384.  
  385.         fit = MIN( length, BLOCK_SIZE - mb->filled );
  386.         CopyMem( data, &mb->data[ mb->filled ], fit );
  387.  
  388.         data += fit;
  389.         length -= fit;
  390.         mb->filled += fit;
  391.         buffer_size += fit;
  392.     }
  393.     return TRUE;
  394. }
  395.  
  396. BOOL AppendVarNum( ULONG num )
  397. {    UBYTE                var[ 6 ],
  398.                         varindex = 6;
  399.  
  400.     var[ --varindex ] = num & 0x7f;
  401.     num >>= 7;
  402.  
  403.     while (num)
  404.     {    var[ --varindex ] = num | 0x80;
  405.         num >>= 7;
  406.     }
  407.  
  408.     return (AppendBytes( var + varindex, 6 - varindex ));
  409. }
  410.  
  411.     /*    Set pointer to beginning of buffer */
  412.  
  413. void BufferStart( struct memory_block **block, LONG *offset )
  414. {    *offset = 0;
  415.     *block = (struct memory_block *)memory_list.lh_Head;
  416.     read_running_status = 0;
  417. }
  418.  
  419.     /*    Read 1 MIDI message from the buffer */
  420.  
  421. WORD GetNextBufferByte( struct memory_block **block, LONG *offset )
  422. {    struct memory_block    *mb = *block;
  423.  
  424.     while (mb->node.mln_Succ != 0)                /* while not end of list        */
  425.     {
  426.         if (*offset < mb->filled)                /* If more bytes in block        */
  427.             return mb->data[ (*offset)++ ];        /* Return next byte from buffer    */
  428.  
  429.         *offset = 0;                            /* Go to next block                */
  430.         *block = mb = (struct memory_block *)mb->node.mln_Succ;
  431.     }
  432.     return -1;
  433. }
  434.  
  435. LONG GetVarNum( struct memory_block **block, LONG *offset )
  436. {    LONG                val = 0;
  437.     WORD                byte;
  438.  
  439.     do
  440.     {    byte = GetNextBufferByte( block, offset );
  441.         if (byte < 0) return -1;
  442.         val = (val << 7) + (byte & 0x7f);
  443.     } while (byte & 0x80) ;
  444.  
  445.     return val;
  446. }
  447.  
  448.     /*    Read 1 MIDI message from the buffer */
  449.  
  450.     /*    REM: This could use some error checks to make sure that the
  451.         data bytes written don't have high bits set.
  452.     */
  453.  
  454. LONG GetNextBufferMessage( struct memory_block **block, LONG *offset, UBYTE *buffer, LONG buffersize )
  455. {    WORD                midibyte;
  456.     LONG                midilength = 0,
  457.                         delta,
  458.                         i;
  459.  
  460.     for (;;)
  461.     {
  462.             /*    Get status byte */
  463.  
  464.         delta = GetVarNum( block, offset );        /* skip over delta time            */
  465.         if (delta < 0) return 0;
  466.  
  467.         midibyte = GetNextBufferByte( block, offset );
  468.         if (midibyte < 0) return 0;
  469.  
  470.         if (midibyte == 0xff)                    /* meta-event                    */
  471.         {
  472.                 /*    Get meta-event subtype (ignored for now) */
  473.  
  474.             if ((midibyte = GetNextBufferByte( block, offset )) < 0) return 0;
  475.  
  476.                 /*    Get meta-event length */
  477.  
  478.             if ((midilength = GetNextBufferByte( block, offset )) < 0) return 0;
  479.  
  480.                 /*    Skip over meta-event data */
  481.  
  482.             while (midilength > 0)
  483.             {    if (GetNextBufferByte( block, offset ) < 0) return 0;
  484.                 midilength--;
  485.             }
  486.         }
  487.         else if (midibyte & 0x80)                /* if status byte                */
  488.         {
  489.             read_running_status = midibyte;        /* set running status            */
  490.  
  491.                 /*    Complete sysex message */
  492.  
  493.             if (midibyte == MS_SysEx)            /* check system exclusive        */
  494.             {                                    /* get exclusive length            */
  495.                 midilength = GetVarNum( block, offset );
  496.                 if (midilength < 0)                /* check error                    */
  497.                     return midilength;
  498.                                                 /* clip to buffer length        */
  499.                 if (midilength + 1 >= buffersize) midilength = buffersize - 1;
  500.                 *buffer++ = MS_SysEx;            /* write SysEx byte                */
  501.                 for (i=0; i<midilength; i++)    /* write other bytes            */
  502.                 {    if ((*buffer++ = GetNextBufferByte( block, offset )) < 0) return 0;
  503.                 }
  504.                 return midilength + 1;
  505.             }
  506.  
  507.                 /*    Incomplete sysex message (some MidiFiles have this) */
  508.  
  509.             if (midibyte == 0xF7)                /* special sysex                */
  510.             {                                    /* get exclusive length            */
  511.                 midilength = GetVarNum( block, offset );
  512.                 if (midilength < 0)                /* check error                    */
  513.                     return midilength;
  514.                                                 /* clip to buffer length        */
  515.                 if (midilength >= buffersize) midilength = buffersize;
  516.                 for (i=0; i<midilength; i++)    /* write other bytes            */
  517.                 {    if ((*buffer++ = GetNextBufferByte( block, offset )) < 0) return 0;
  518.                 }
  519.                 return midilength;
  520.             }
  521.  
  522.             midilength = MidiMsgLen( midibyte );
  523.  
  524.             if (midilength > 0)                /* if normal message            */
  525.             {    *buffer++ = midibyte;        /* put status byte in buffer    */
  526.  
  527.                 for (i=1; i<midilength; i++) /* copy data bytes to buffer    */
  528.                 {    if ((*buffer++ = GetNextBufferByte( block, offset )) < 0) return 0;
  529.                 }
  530.                 return midilength;            /* return length                */
  531.             }
  532.         }
  533.         else    /* no status byte, use running status */
  534.         {
  535.             midilength = MidiMsgLen( read_running_status );
  536.             *buffer++ = read_running_status;
  537.             *buffer++ = midibyte;
  538.             for (i=2; i<midilength; i++)
  539.             {    if ((*buffer++ = GetNextBufferByte( block, offset )) < 0) return 0;
  540.             }
  541.             return midilength;
  542.         }
  543.     }
  544. }
  545.  
  546.     /* MIDI Input functions */
  547.  
  548. void HandleMidi( struct TMData *TMData )
  549. {    MidiMsg        mm;
  550.     ULONG        time = 0;
  551.  
  552.     while (GetMidi( midi, &mm ))
  553.     {
  554.         ULONG    length = MidiMsgLen( mm.mm_Status );
  555.  
  556.             /*    REM: Append a time delta value for standard MIDI? ? */
  557.  
  558.         if (mm.mm_Status == MS_SysEx)
  559.         {    UBYTE        temp_ssx[ 1024 ];
  560.  
  561.             length = GetSysEx( midi, temp_ssx, sizeof temp_ssx );
  562.  
  563.             if (length >= sizeof temp_ssx)
  564.             {    /* error, message too long for buffer */
  565.             }
  566.  
  567.                 /*    Store into the buffer in standard MIDI style:
  568.                     --    Variable length delta time (always zero here).
  569.                     --    System Exclusive byte (0xF0)
  570.                     --    Variable-length number of bytes in message.
  571.                     --    Actual message bytes, including EOX (0xf7);
  572.                 */
  573.  
  574.             if (    AppendVarNum( time - buffer_time ) == FALSE
  575.                  || AppendBytes( &temp_ssx[ 0 ], 1 ) == FALSE
  576.                  || AppendVarNum( length - 1 ) == FALSE
  577.                  || AppendBytes( &temp_ssx[ 1 ], length - 1 ) == FALSE )
  578.             {    /* error... */
  579.             }
  580.         }
  581.         else if (length)                        /* only save messages with length    */
  582.         {
  583.                 /*    First, append the delta-tie to the stream */
  584.  
  585.             if ( AppendVarNum( time - buffer_time ) == FALSE )
  586.             {    /* error */
  587.             }
  588.             else if (mm.mm_Status < 0xf0 && mm.mm_Status == buffer_running_status)
  589.             {
  590.                     /*    If the status byte is the same as the last,
  591.                         The don't need to write it...
  592.                     */
  593.  
  594.                 if ( AppendBytes( &mm.mm_Data[1], length-1 ) == FALSE )
  595.                 {    /* error */
  596.                 }
  597.             }        /*    Otherwise, write out the status byte */
  598.             else if (AppendBytes( mm.mm_Data, length ) == FALSE)
  599.             {    /* error */
  600.             }
  601.         }
  602.         buffer_running_status = mm.mm_Status;
  603.         buffer_time = time;
  604.     }
  605.  
  606.     GT_SetGadgetAttrs(
  607.         GadgetInfo_BYTESINB.Gadget,
  608.         WindowInfo_CAPTUREM.Window,
  609.         NULL,
  610.         GTNM_Number, buffer_size,
  611.         TAG_END );
  612. }
  613.  
  614.     /* Send MIDI data out again */
  615.  
  616. UWORD __chip WaitPData[] =
  617. {   0x0000, 0x0000,
  618.     0x0400, 0x07C0,
  619.     0x0000, 0x07C0,
  620.     0x0100, 0x0380,
  621.     0x0000, 0x07E0,
  622.     0x07C0, 0x1FF8,
  623.     0x1FF0, 0x3FEC,
  624.     0x3FF8, 0x7FDE,
  625.     0x3FF8, 0x7FBE,
  626.     0x7FFC, 0xFF7F,
  627.     0x7EFC, 0xFFFF,
  628.     0x7FFC, 0xFFFF,
  629.     0x3FF8, 0x7FFE,
  630.     0x3FF8, 0x7FFE,
  631.     0x1FF0, 0x3FFC,
  632.     0x07C0, 0x1FF8,
  633.     0x0000, 0x07E0,
  634.     0x0000, 0x0000,             /* reserved, must be NULL */
  635. };
  636.  
  637. void SendMIDI( struct TMData *TMData )
  638. {    UBYTE                temp_msg[ 1024 ];
  639.     LONG                offset;
  640.     struct memory_block *block;
  641.     LONG                length;
  642.  
  643.     SetPointer( WindowInfo_CAPTUREM.Window, WaitPData, 16, 16, -7, -7 );
  644.     BufferStart( &block, &offset );
  645.  
  646.     while (length = GetNextBufferMessage( &block, &offset, temp_msg, sizeof temp_msg ))
  647.     {    MidiMsg            mm;
  648.  
  649.         if (length < 0) break;
  650.  
  651.         if (temp_msg[0] == MS_SysEx) PutSysEx ( out_link, temp_msg );
  652.         else
  653.         {    CopyMem( temp_msg, mm.mm_Data, 3 );
  654.             PutMidi( out_link,
  655.                      (temp_msg[0] << 24) | (temp_msg[1] << 16) | (temp_msg[2] << 8) );
  656.         }
  657.     }
  658.     ClearPointer( WindowInfo_CAPTUREM.Window );
  659. }
  660.  
  661.     /* MIDI Load / Save functions */
  662.  
  663. struct smfHeader {
  664.     WORD                format;
  665.     WORD                tracks;
  666.     WORD                division;
  667. };
  668.  
  669.  
  670. void FileError( struct TMData *TMData, LONG error, char *filename )
  671. {    TM_Request( WindowInfo_CAPTUREM.Window,
  672.                 (UBYTE *)TEXT_ERROR,
  673.                 "Error writing file '%s', code %ld",
  674.                 "Continue", NULL,
  675.                 filename, error );
  676. }
  677.  
  678.     /*    Note that buffer has no running status. Should we compress? */
  679.  
  680. void SaveMIDI( struct TMData *TMData, char *filename )
  681. {
  682.     LONG                offset;
  683.     struct memory_block *block;
  684.     struct smfHeader    smf;
  685.     LONG                track_size = 0;
  686.     LONG                header_length = sizeof smf;
  687.     BPTR                fh;
  688.     LONG                error = 0;
  689.     static UBYTE        end_event[] = { 0x00, 0xFF, 0x2F, 0x00 };
  690.  
  691.     SetPointer( WindowInfo_CAPTUREM.Window, WaitPData, 16, 16, -7, -7 );
  692.  
  693.     smf.format = 0;
  694.     smf.tracks = 1;
  695.     smf.division = 192;
  696.  
  697.         /*    Start by counting the number of messages and calculating the track size */
  698.  
  699.     BufferStart( &block, &offset );
  700.     track_size = buffer_size;
  701.     if (!has_end_event) track_size += 4;
  702.  
  703.     if (fh = Open( filename, MODE_NEWFILE ))
  704.     {    struct memory_block    *mb;
  705.  
  706.         BufferStart( &block, &offset );
  707.  
  708.         if (   (FWrite( fh, "MThd",                 4,             1 ) != 1)
  709.             || (FWrite( fh, (STRPTR)&header_length, sizeof (LONG), 1 ) != 1)
  710.             || (FWrite( fh, (STRPTR)&smf,            sizeof smf,    1 ) != 1)
  711.             || (FWrite( fh, "MTrk",                    4,             1 ) != 1)
  712.             || (FWrite( fh, (STRPTR)&track_size,    sizeof (LONG), 1 ) != 1) )
  713.         {    error = IoErr();
  714.         }
  715.  
  716.         for (mb = (struct memory_block *)memory_list.lh_Head;
  717.              mb->node.mln_Succ;
  718.              mb = (struct memory_block *)mb->node.mln_Succ )
  719.         {
  720.             if (FWrite( fh, mb->data, mb->filled, 1 ) != 1)
  721.             {    error = IoErr();
  722.             }
  723.         }
  724.         if (!has_end_event &&
  725.             (FWrite( fh, end_event, sizeof end_event, 1 ) != 1) )
  726.         {    error = IoErr();
  727.         }
  728.  
  729.         /*    Printf("%02.2lx length=%ld\n", temp_msg[ 0 ], length ); */
  730.         Close( fh );
  731.     }
  732.     ClearPointer( WindowInfo_CAPTUREM.Window );
  733.  
  734.     if (error) FileError( TMData, error, filename );
  735. }
  736.  
  737. #if 0
  738. BOOL StuffBytes( struct TMData *TMData, UBYTE *data, LONG length, char *filename )
  739. {
  740.     if (AppendBytes( data, length ) == FALSE)
  741.     {    TM_Request( WindowInfo_CAPTUREM.Window,
  742.                     (UBYTE *)TEXT_ERROR,
  743.                     "Not enough memory to load file '%s'",
  744.                     "Continue", NULL,
  745.                     filename );
  746.         break;
  747.     }
  748. }
  749. #endif
  750.  
  751. void LoadMIDI( struct TMData *TMData, char *filename )
  752. {    BPTR                fh;
  753.     struct smfHeader    smf;
  754.     LONG                length;
  755.     LONG                error = 0;
  756.     struct {
  757.         LONG            chunkID,
  758.                         chunkLength;
  759.     } chunkHeader;
  760.  
  761.     SetPointer( WindowInfo_CAPTUREM.Window, WaitPData, 16, 16, -7, -7 );
  762.  
  763.     if (fh = Open( filename, MODE_OLDFILE ))
  764.     {
  765.  
  766.         if (FRead( fh, (STRPTR)&chunkHeader, sizeof chunkHeader, 1 ) != 1)
  767.         {    error = IoErr();
  768.         }
  769.         else if (chunkHeader.chunkID != 'MThd')    /* Read file header    ID        */
  770.         {    TM_Request( WindowInfo_CAPTUREM.Window,
  771.                         (UBYTE *)TEXT_ERROR,
  772.                         "This is not a Standard MIDI File",
  773.                         "Continue", NULL, NULL );
  774.         }
  775.         else if (chunkHeader.chunkLength != sizeof smf) /* Read file header length    */
  776.         {    TM_Request( WindowInfo_CAPTUREM.Window,
  777.                         (UBYTE *)TEXT_ERROR,
  778.                         "Bad size for MIDI file header",
  779.                         "Continue", NULL, NULL );
  780.         }                                        /* Read file header            */
  781.         else if (FRead( fh, (STRPTR)&smf, sizeof smf, 1 ) != 1)
  782.         {    error = IoErr();
  783.         }
  784.         else
  785.         {    UBYTE        temp_buffer[512];
  786.  
  787.             for (;;)
  788.             {
  789.                     /* Read the track header ID */
  790.  
  791.                 if (FRead( fh, (STRPTR)&chunkHeader, sizeof chunkHeader, 1 ) != 1)
  792.                 {    error = IoErr();
  793.                     break;
  794.                 }
  795.  
  796.                     /* If it's not a track, skip it */
  797.  
  798.                 if (chunkHeader.chunkID != 'MTrk')
  799.                 {    Seek( fh, chunkHeader.chunkLength, OFFSET_CURRENT );
  800.                     if (error = IoErr()) break;
  801.                     continue;
  802.                 }
  803.  
  804.                 length = chunkHeader.chunkLength;
  805.                 ClearBuffer();                    /* clear out the old data        */
  806.  
  807.                     /* Read entire track */
  808.  
  809.                 while (length > 0)
  810.                 {    LONG    block_size = MIN( length, sizeof temp_buffer );
  811.  
  812.                     if (FRead( fh, temp_buffer, block_size, 1 ) != 1)
  813.                     {    error = IoErr();
  814.                         break;
  815.                     }
  816.  
  817.                     if (AppendBytes( temp_buffer, block_size ) == FALSE)
  818.                     {    TM_Request( WindowInfo_CAPTUREM.Window,
  819.                                     (UBYTE *)TEXT_ERROR,
  820.                                     "Not enough memory to load file '%s'",
  821.                                     "Continue", NULL,
  822.                                     filename );
  823.                         break;
  824.                     }
  825.                     length -= block_size;
  826.                 }
  827.                 break;
  828.             }
  829.             has_end_event = TRUE;
  830.         }
  831.         Close( fh );
  832.     }
  833.     ClearPointer( WindowInfo_CAPTUREM.Window );
  834.  
  835.     if (error) FileError( TMData, error, filename );
  836. }
  837.